package com.asura.ops.sync.sdk.handle;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.asura.ops.sync.sdk.model.MqPayload;
import com.asura.ops.sync.sdk.sql.SqlSentence;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

/**
 * @author: zouyang
 * @date: 2022/9/12 2:32
 * @description:
 */
@Slf4j
public abstract class AbstractSyncHandler<T> implements SyncHandler<Object> {

    //表，实体映射， <mdm_sku, MdmSkuEntity>
    protected Map<String, Class> tableEntityClassMap = Maps.newConcurrentMap();

    /**
     * 源表表名 和 目标表名 映射
     */
    protected Map<String, String> sourceTargetTableMap = Maps.newConcurrentMap();


    //写入目标数据库链接
    protected List<DataSource> dataSourceList = Lists.newArrayList();

    //连接池使用
    protected List<DruidPooledConnection> druidPooledConnections = Lists.newArrayList();

    public void configTableEntityClassMap(Map<String, Class> map) {
        this.tableEntityClassMap = map;
    }

    @Override
    public void configDatasourceList(List<DataSource> dataSourceList) {
        this.dataSourceList = dataSourceList;
    }

    public void configSourceTargetTableMap(Map<String, String> tableMap) {
        this.sourceTargetTableMap = tableMap;
    }

    @Override
    public void handle(MqPayload payload) throws Exception {
        Class cls = tableEntityClassMap.get(payload.getSource().getTable());

        if (cls == null) {
            log.info("无对应实体，不处理");
            return;
        }

        Object after = JSONUtil.toBean(JSONUtil.toJsonStr(payload.getAfter()), cls);
        Object before = JSONUtil.toBean(JSONUtil.toJsonStr(payload.getBefore()), cls);

        switch (payload.getChangeType()) {
            case 1:
                insert(getTargetTableName(payload.getSource().getTable()), cls, after);
                break;
            case 2:
                update(getTargetTableName(payload.getSource().getTable()), cls, after);
                break;
            case 3:
                removeById(getTargetTableName(payload.getSource().getTable()), cls, before);
                break;
            case 4:
                insertOrUpdate(getTargetTableName(payload.getSource().getTable()), cls, after);
                break;
        }
    }

    @Override
    public void insert(String tableName, Class clazz, Object t) throws SQLException {
        if (t == null) {
            return;
        }
        String insertSql = SqlSentence.getInsertSql(tableName, clazz, t);
        log.info("insertSql:{}", insertSql);

        executeSql(insertSql);
    }

    @Override
    public void update(String tableName, Class clazz, Object t) throws SQLException {
        if (t == null) {
            return;
        }

        String updateSql = SqlSentence.getUpdateSql(tableName, clazz, t);
        log.info("updateSql:{}", updateSql);

        executeSql(updateSql);
    }

    @Override
    public void removeById(String tableName, Class<Object> clazz, Object t) throws SQLException {
        Field field = ReflectUtil.getField(t.getClass(), "id");
        Object idValue = ReflectUtil.getFieldValue(t, field);

        String deleteSql = "delete from " + tableName + " where id = " + idValue;
        log.info("deleteSql:{}", deleteSql);

        executeSql(deleteSql);
    }


    @Override
    public void insertOrUpdate(String tableName, Class clazz, Object t) throws SQLException {
        String selectSql = SqlSentence.getSelectSql(tableName, t);
        String updateSql = SqlSentence.getUpdateSql(tableName, clazz, t);
        String insertSql = SqlSentence.getInsertSql(tableName, clazz, t);

        executeInsertOrUpdate(selectSql, updateSql, insertSql);
    }

    /**
     * 获取目标表名
     *
     * @param sourceTableName
     * @return
     */
    private String getTargetTableName(String sourceTableName) {
        if (CollectionUtils.isEmpty(sourceTargetTableMap)) {
            return sourceTableName;
        }
        if (sourceTargetTableMap.containsKey(sourceTableName)) {
            return sourceTargetTableMap.get(sourceTableName);
        }
        return sourceTableName;
    }

    /**
     * 插入或更新
     *
     * @param selectSql
     * @param updateSql
     * @param insertSql
     */
    private void executeInsertOrUpdate(String selectSql, String updateSql, String insertSql) throws SQLException {
        if (CollectionUtils.isEmpty(dataSourceList)) {
            return;
        }

        for (DataSource dataSource : dataSourceList) {
            DruidPooledConnection connection = ((DruidDataSource) dataSource).getConnection();

            Statement statement = connection.createStatement();

            ResultSet resultSet = statement.executeQuery(selectSql);

            if (resultSet.next()) {
                //存在
                statement.execute(updateSql);
                log.info("executeInsertOrUpdate updateSql:{}", updateSql);
            } else {
                statement.execute(insertSql);
                log.info("executeInsertOrUpdate insertSql:{}", insertSql);
            }
            statement.close();
            connection.close();
        }
    }

    /**
     * 执行sql
     *
     * @param sql
     */
    private void executeSql(String sql) throws SQLException {
        if (CollectionUtils.isEmpty(dataSourceList)) {
            return;
        }

        for (DataSource dataSource : dataSourceList) {
            DruidPooledConnection connection = ((DruidDataSource) dataSource).getConnection();

            Statement statement = connection.createStatement();

            statement.execute(sql);
            statement.close();

            connection.close();
        }
    }
}
